use bracket_lib::prelude::*;

const SCREEN_WIDTH: i32 = 80;
const SCREEN_HEIGHT: i32 = 50;
const FRAME_TIME: f32 = 75.0;

struct State {
    bird: Bird,
    frame_time: f32,
    window: GameWindow,
    obstacle: Obstacle,
    score: i32,
}

struct Bird {
    x: i32,
    y: i32,
    velocity: f32,
}

impl Bird {
    fn new(x: i32, y: i32) -> Self {
        Bird {
            x: x,
            y: y,
            velocity: 0.0,
        }
    }

    fn render(&self, ctx: &mut BTerm) {
        ctx.set(0, self.y, YELLOW, BLACK, to_cp437('@'));
    }

    fn g_and_move(&mut self) {
        if self.velocity < 2.0 {
            self.velocity += 0.2;
        }
        self.y += self.velocity as i32;
        self.x += 1;

        if self.y < 0 {
            self.y = 0;
        }
    }

    fn fly(&mut self) {
        self.velocity = -2.0;
    }
}

impl GameState for State {
    fn tick(&mut self, ctx: &mut BTerm) {
        match self.window {
            GameWindow::MainMenu => self.main_window(ctx),
            GameWindow::Game => self.game_window(ctx),
            GameWindow::GameOver => self.game_over_window(ctx),
        }
    }
}

enum GameWindow {
    MainMenu,
    Game,
    GameOver,
}

impl State {
    fn new() -> Self {
        State {
            bird: Bird::new(0, 0),
            frame_time: 0.0,
            window: GameWindow::MainMenu,
            obstacle: Obstacle::new(SCREEN_WIDTH, 0),
            score: 0,
        }
    }

    fn main_window(&mut self, ctx: &mut BTerm) {
        ctx.cls();
        ctx.print_centered(15, "Welcome to the Game!");
        ctx.print_centered(40, "Press [Enter] to start.");
        ctx.print_centered(41, "Press [Esc] to exit.");

        if let Some(key) = ctx.key {
            match key {
                VirtualKeyCode::Return => self.restart_game(),
                VirtualKeyCode::Escape => ctx.quitting = true,
                _ => {}
            }
        }
    }

    fn game_window(&mut self, ctx: &mut BTerm) {
        ctx.cls_bg(NAVY);
        if self.score == 0 {
            ctx.print_centered(SCREEN_HEIGHT / 2, "Press [Space] to fly up.");
        }

        self.frame_time += ctx.frame_time_ms;
        if self.frame_time > FRAME_TIME {
            self.frame_time = 0.0;
            self.bird.g_and_move();
        }

        if let Some(VirtualKeyCode::Space) = ctx.key {
            self.bird.fly();
        }

        self.bird.render(ctx);
        ctx.print(
            SCREEN_WIDTH - (format!("Score: {}", &self.score).len() as i32),
            1,
            &format!("Score: {}", self.score),
        );

        self.obstacle.render(ctx, self.bird.x);
        if self.bird.x > self.obstacle.x {
            self.score += 1;
            self.obstacle = Obstacle::new(self.bird.x + SCREEN_WIDTH, self.score);
        }

        if self.obstacle.hit_test(&self.bird) || self.bird.y > SCREEN_HEIGHT {
            //self.game_over_window(ctx);
            self.window = GameWindow::GameOver;
        }
    }

    fn game_over_window(&mut self, ctx: &mut BTerm) {
        //ctx.cls();
        ctx.print_color_centered(15, WHITE, BLACK, "Game Over!");
        ctx.print_color_centered(25, WHITE, BLACK, &format!("Your score: {}", self.score));
        ctx.print_color_centered(40, WHITE, BLACK, "Press [Enter] to start.");
        ctx.print_color_centered(41, WHITE, BLACK, "Press [Esc] to exit.");
        if let Some(key) = ctx.key {
            match key {
                VirtualKeyCode::Return => self.restart_game(),
                VirtualKeyCode::Escape => ctx.quitting = true,
                _ => {}
            }
        }
    }

    fn restart_game(&mut self) {
        self.bird = Bird::new(5, 15);
        self.frame_time = 0.0;
        self.window = GameWindow::Game;
        self.obstacle = Obstacle::new(SCREEN_WIDTH, 0);
        self.score = 0;
    }
}

struct Obstacle {
    x: i32,
    gap_y: i32,
    size: i32,
}

impl Obstacle {
    fn new(x: i32, score: i32) -> Self {
        if score == 0 {
            return Obstacle {
                x,
                gap_y: 25,
                size: 30,
            };
        }
        let mut random = RandomNumberGenerator::new();
        Obstacle {
            x,
            gap_y: random.range(10, 40),
            size: i32::max(2, 20 - score),
        }
    }

    fn render(&mut self, ctx: &mut BTerm, player_x: i32) {
        let screen_x = self.x - player_x;
        let half_size = self.size / 2;

        for y in 0..self.gap_y - half_size {
            ctx.set(screen_x, y, RED, BLACK, to_cp437('#'));
        }

        for y in self.gap_y + half_size..SCREEN_HEIGHT {
            ctx.set(screen_x, y, RED, BLACK, to_cp437('#'));
        }
    }

    fn hit_test(&mut self, bird: &Bird) -> bool {
        bird.x == self.x
            && (bird.y < self.gap_y - &self.size / 2 || bird.y > self.gap_y + &self.size / 2)
    }
}

fn main() -> BError {
    //println!("fisrt output");
    let ctx = BTermBuilder::simple80x50()
        .with_title("bird_game")
        .build()?;

    //println!("window created");

    main_loop(ctx, State::new())
}
